package com.ashafa.server;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import com.ashafa.cache.DomCache;
import com.ashafa.fileutils.*;

import org.mozilla.javascript.*;
import org.apache.log4j.*;
import org.envjson.*;


public class Servlet extends HttpServlet {
	
	private static final Logger logger = Logger.getLogger(Servlet.class.getName()); 

	private Script envJs;
	
	private String contextsFolder;
	private JSONArray urlsArray;
	private String servletContextPath;
	private Boolean urlsWatcherIsRunning = false;
	
	private ScriptableObject coreScope;
	private ScriptableObject sharedScope;
	private DomCache domCache;
	private Routing routing;
	
	private void setURLs() throws JSONException{
		urlsArray = new JSONObject(FetchFile.readFile(servletContextPath + "/urls.json", "{urls:[]}")).getJSONArray("urls");
	}
	
	
	private void startUrlsWatcher() throws Exception{
		
		TimerTask urlsWatcherTask = new FileWatcher(new File(servletContextPath+ "/urls.json")){
			protected void onChange(File file) throws Exception {
				urlsArray = null;
			}
		};
			
		new Timer().schedule(urlsWatcherTask, new Date(), 1000);
		
		urlsWatcherIsRunning = true;
	}
	
	
		
	public Servlet(String contextsFolder, org.mortbay.jetty.servlet.Context servletContext) {
		this.contextsFolder = contextsFolder;
	}
	
	
	
	
	public void init() throws ServletException {
		
		servletContextPath = contextsFolder + getServletContext().getContextPath();
		domCache = new DomCache();
		routing = new Routing();
		
		
		
		Context cx = new CustomContextFactory().enterContext();
		
		try
		{
			coreScope = new ImporterTopLevel(cx);
						
			coreScope.put("$log", coreScope,  Context.javaToJS(Logger.getLogger(getServletContext().getContextPath()), coreScope));
			
			cx.evaluateString(coreScope, FetchFile.readFile("./core-js/env.js", ""), "core/env.js", 1, null);
			cx.evaluateString(coreScope, FetchFile.readFile("./core-js/date.js", ""), "core/date.js", 1, null);
			cx.evaluateString(coreScope, FetchFile.readFile("./core-js/jquery.js", ""), "core/jquery.js", 1, null);
			cx.evaluateString(coreScope, FetchFile.readFile("./core-js/core.js", ""), "core/core.js", 1, null);
			
			envJs = cx.compileString(FetchFile.readFile("./core-js/env.js", ""), "env ", 1, null);
			
			ScriptableObject core = (ScriptableObject)coreScope.get("$core", coreScope);
			
			ScriptableObject.putProperty(core, "javaDomCache", Context.javaToJS(domCache, core));
			ScriptableObject.putProperty(core, "servletContextPath", servletContextPath);
			ScriptableObject.putProperty(core, "controllerManager", Context.javaToJS(new ControllerManager(getServletContext().getContextPath(), servletContextPath, coreScope), core));
			
			
			sharedScope = (ScriptableObject)cx.newObject(coreScope);
			sharedScope.setPrototype(coreScope);
			sharedScope.setParentScope(null);
			
			
			String servletPluginsPath = "./plugins";
			File pluginsDir = new File(servletPluginsPath);
			String[] pluginsDirList = pluginsDir.list();
			
			for(int i = 0; i < pluginsDirList.length; i++)
			{
				if(pluginsDirList[i].endsWith(".js"))
				{
					cx.evaluateString(sharedScope, FetchFile.readFile(servletPluginsPath+ "/" + pluginsDirList[i], ""), servletPluginsPath+"/"+pluginsDirList[i]+"", 1 , null);
				}
			}
			
			cx.evaluateString(sharedScope, FetchFile.readFile(servletContextPath+ "/shared.js", ""), servletContextPath+ "/shared.js" , 1, null);
			
			
			
			if(!urlsWatcherIsRunning)
			{
				try{
					startUrlsWatcher();
				} catch(Exception e){
					throw new ServletException(e);
				}
			}
				
			((Function)core.get("init", core)).call(cx, core, core, null);	
						
		} finally {
			Context.exit();
		}
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException	{
	
		final long start = System.currentTimeMillis();
						
		String uri = request.getRequestURI();
		if(urlsArray == null)
		{
			try{
				setURLs();
			} catch (JSONException e) {
				throw new ServletException(e);
			}
		}
		RoutingResult routingResult = routing.route(uri, urlsArray, getServletContext().getContextPath());
		
		if(routingResult.getView() != null)
		{
			Context cx = new CustomContextFactory().enterContext();
			
			try
			{
				Scriptable threadScope = cx.newObject(sharedScope);
				threadScope.setPrototype(sharedScope);
				threadScope.setParentScope(null);
				
				envJs.exec(cx, threadScope);
				ScriptableObject core = (ScriptableObject)coreScope.get("$core", threadScope);
							
				response.setStatus(200);
				response.setContentType("text/html; charset=UTF-8");
				
				Object getOutputArgs[] = {routingResult.getView(), request, response, uri, routingResult.getUriTokensJsonString()};
				Object result = ((Function)core.get("getOutput", threadScope)).call(cx, threadScope, threadScope, getOutputArgs);
							
				if(!response.isCommitted())
					response.getWriter().println(Context.toString(result));
				
				final long end = System.currentTimeMillis();
				System.out.println("Time: " + (end - start) + " ms");
							
			} catch (Exception e) {
				throw new ServletException(e);
			} finally {
				Context.exit();
			}
		
		} else {
			response.sendError(404);
		}	
		
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
